#include "player.h"

QSvgRenderer * card::RENDERER = 0;
QSizeF card::CARDSIZE=QSizeF(0,0);


int player::cards_in_suit(const card::CSUIT & suit)
{
    int cnt(0);
    for (int x=0; x < this->hand.count(); x++)
        if ( hand[x]->suit == suit)
            cnt++;
    return cnt;
}

int player::cards_in_suit(const card::CSUIT & suit, const QList<card *> & cards)
{
    int cnt(0);
    for (int x=0; x < cards.count(); x++)
        if ( cards[x]->suit == suit)
            cnt++;
    return cnt;
}


card::card(CSUIT s, CVALUE v, player *setowner): suit(s), value(v), owner(setowner)
{
    svg.setSharedRenderer(RENDERER);
    svg.setElementId(getSVGString());
    index=this->sv2num();
}

card::card(int num, player *setowner) :  owner(setowner)
{
    num%=52;
    suit= static_cast<CSUIT> (num/13);
    value= static_cast<CVALUE> (num - (13 * suit));
    svg.setSharedRenderer(RENDERER);
    svg.setElementId(getSVGString());
    index=num;
}

/*
int card::sv2num(const CSUIT & s, const CVALUE & v)
{
   return s*13+v;
}
*/

int card::sv2num() const
{
    return static_caset<int>(suit)*13 + statc_cast<int>(value);
}

QString card::getSVGString()
{
    QString prefix;
    QString suffix;
    switch (suit) {
    case SPADE :
        suffix="spade"; break;
    case HEART :
        suffix="heart"; break;
    case DIAMOND :
        suffix="diamond"; break;
    case CLUB :
        suffix="club"; break;
    default:
        suffix="";
    }

    switch (value) {
    case JACK :
        prefix="jack"; break;
    case QUEEN:
        prefix="queen"; break;
    case KING:
        prefix="king";break;
    case ACE:
        prefix="1";break;
    case TEN:
        prefix="10"; break;
    default:
        prefix=QString('0' + (char)value + 2);
    }

    return prefix + "_" + suffix;
}

bool card::operator >(const card & right)
{
    if (this->suit == right.suit)
        return (this->value > right.value);
    else if (right.suit == SPADE)
        return false;
    else return true; // either left card is spade, or right card is off suit
}

bool card::operator < (const card & right) //sorting
{
    // return ( sv2num() < right.sv2num() );
    return ( sv2num() > right.sv2num() ); // reversing, much of the bidding code now depends on hand order, do not change!
}


int player::conservative_bid()
{
    int tmp,bid=0;
    //bool go_nill=true;
    int your_spades = cards_in_suit(card::SPADE), available_spades = 13-your_spades;

    for (card::CSUIT suit=card::DIAMOND; suit<card::SPADE; suit=(card::CSUIT)(suit+1))
    {
        int num_in_suit = cards_in_suit(suit);
        if (num_in_suit < 2) //Points for spaing null/singleton suit
        {
            tmp = qMin(2-num_in_suit,your_spades);
            your_spades-=tmp;
            bid+=tmp;
        }

        if (num_in_suit == 1 || num_in_suit==5 )
            if (value_in_suit(suit) == card::ACE)
                bid++;

        if (num_in_suit == 2 || num_in_suit==4)
        {
            if (value_in_suit(suit) >= card::KING)
                bid++;
            if (value_in_suit(suit,1) == card::KING)
                bid++;
        }

        if (num_in_suit == 3)
        {
            if (value_in_suit(suit) >= card::QUEEN)
                bid++;
            if (value_in_suit(suit,1) >= card::QUEEN)
                bid++;
            if (value_in_suit(suit,2) >= card::QUEEN)
                bid++;
        }
        //if you have >=6 in one suit, chances are you arn't taking tricks with that suit
    }

    int pos=0;
    card::CVALUE value_to_beat = card::KING;

    available_spades-=2; //Assuming at least one full spades trick (-1) accounted for below your_spades already subtracted
    while (your_spades > 0 && available_spades>0)
    {
        if (value_in_suit(card::SPADE,pos) > value_to_beat)
        {
            ++pos; ++ bid;
        }
        value_to_beat = (card::CVALUE)(value_to_beat - 1);
        --your_spades;
        --available_spades;
    }
    return bid + your_spades;
}

//play helper functions
bool player::spades_broken(const QList<card *> & discard)
{
    return cards_in_suit(card::SPADE,discard) > 0;
}

card * player::takecard(const card::CSUIT & s, const int & position=0 )
{
    int suit_start_index(-1);
    for(int x(0); x < this->hand.count(); ++x)
        if (hand[x]->suit == s)
        { suit_start_index=x; break;}
    if ( suit_start_index != -1 && suit_start_index + position < hand.count() )
        return hand.takeAt(suit_start_index+position);
    else return NULL;    
}

card * player::peekcard(const card::CSUIT & s, const int & position=0 )
{
    int suit_start_index(-1);
    for(int x(0); x < this->hand.count(); ++x)
        if (hand[x]->suit == s)
        { suit_start_index=x; break;}
    if ( suit_start_index != -1 && suit_start_index + position < hand.count() )
        return hand[suit_start_index+position];
    else return NULL;
}

//counts number of cards above c in the discard pile to determin if it current top.
bool player::iswinner(const card & c, const QList<card *> & discard)
{
    int number_above=0;
    for (int idx=0; idx<discard.count(); idx++)
        if (c.suit == discard[idx]->suit && c.value < discard[idx]->value)
            number_above++;

    return ( static_cast<int>(c.value) + number_above == 12);  //counts from 0
}

card * player::findwinner(QList<card::CSUIT> suits, const QList<card *> & discard) {
    while( suits.count() )
    {
        QList<card::CSUIT> inner_check(suits);
        while ( inner_check.count() > 1)
        {
            if (cards_in_suit(inner_check[0]) >= cards_in_suit(inner_check[1]))
                inner_check.removeFirst();
            else inner_check.removeAt(1);
        }
        card tmp(inner_check[0],value_in_suit(inner_check[0]));
        if( iswinner(tmp, discard ) )
            return this->takecard(inner_check[0],0);  //play if winner

        for (int idx=0; idx<suits.count(); ++idx) //remove from list and try again
            if ( suits[idx]==inner_check[0])
                suits.removeAt(idx);
    }
    return 0;
}

card * current_winning_card(card * trick[], int num_cards_played=4)  //note non member
{
    card * high_card = trick[0];
    for (int i=1; i<num_cards_played; ++i)
    {
        if (trick[i] > high_card)
            high_card=trick[i];
    }
    return high_card;
}


card * player::conservative_play(card * trick[], int trick_pos, const QList<card *> & discard)
{
    int num_clubs=cards_in_suit(card::CLUB);
    int num_diamonds=cards_in_suit(card::DIAMOND);
    int num_hearts  = cards_in_suit(card::HEART);
    int num_spades  = cards_in_suit(card::SPADE);
    QList< card::CSUIT> tocheck;
    if ( !trick_pos ) //leading
    {
        if (this->isBiddingNil) {
            //PLAY suit you want partner to copy

        } else if (this->partner->isBiddingNil) {
            //TODO: suit partner lead first ?
            if (spades_broken(discard)) //highest spade
                if ( value_in_suit(card::SPADE) > card::TEN )
                    return this->takecard(card::SPADE);

            //null suit, try one with least amount of cards

            if (!this->partner->hasClubs && num_clubs>0)
                tocheck << card::CLUB;
            if (!this->partner->hasDiamonds && num_diamonds>0)
                tocheck << card::DIAMOND;
            if (!this->partner->hasHearts && num_hearts>0)
                tocheck << card::HEART;

            while( tocheck.count() > 1)
            {
                if ( cards_in_suit(tocheck[0]) >= cards_in_suit(tocheck[1]) )
                    tocheck.removeAt(0);
                else tocheck.removeAt(1);
            }

            if( tocheck.count()==1)
                return this->takecard(tocheck[0],cards_in_suit(tocheck[0]));

            //PLAY anything you can win
            tocheck.clear();
            if ( num_clubs) tocheck << card::CLUB;
            if ( num_diamonds ) tocheck << card::DIAMOND;
            if ( num_hearts   ) tocheck << card::HEART;
            if ( num_spades && this->spades_broken(discard)) tocheck << card::SPADE;
            card * c=findwinner(tocheck,discard);
            if (c != 0) return c;

            //highest card of longest suit
            tocheck.clear();
            if ( num_clubs) tocheck << card::CLUB;
            if ( num_diamonds ) tocheck << card::DIAMOND;
            if ( num_hearts   ) tocheck << card::HEART;
            if ( num_spades && this->spades_broken(discard)) tocheck << card::SPADE;
            while (tocheck.count() > 1)
            {
                if ( cards_in_suit(tocheck[0]) >= cards_in_suit(tocheck[1]) )
                    tocheck.removeAt(1);
                else tocheck.removeAt(0);
            }
            if( tocheck.count()==1)
                return this->takecard(tocheck[0],cards_in_suit(tocheck[0]));

            //PLAY top spade (if the rest of the logic worked)
            return hand.takeFirst();

        } else {
            if ( currentbid > tricks + partner->tricks )  { //want to win //could be smarter

                //PLAY anything you can win
                if ( num_clubs) tocheck << card::CLUB;
                if ( num_diamonds ) tocheck << card::DIAMOND;
                if ( num_hearts   ) tocheck << card::HEART;
                if ( num_spades && this->spades_broken(discard)) tocheck << card::SPADE;
                card * c=findwinner(tocheck,discard);
                if (c != 0) return c;

                //play suit parter is nul in (spades to win)
                if (this->partner->hasSpades) {
                    tocheck.clear();
                    if (!this->partner->hasClubs && num_clubs>0)
                        tocheck << card::CLUB;
                    if (!this->partner->hasDiamonds && num_diamonds>0)
                        tocheck << card::DIAMOND;
                    if (!this->partner->hasHearts && num_hearts>0)
                        tocheck << card::HEART;

                    while( tocheck.count() > 1)
                    {
                        if ( cards_in_suit(tocheck[0]) >= cards_in_suit(tocheck[1]) )
                            tocheck.removeAt(0);
                        else tocheck.removeAt(1);
                    }

                    if( tocheck.count()==1)
                        return this->takecard(tocheck[0],cards_in_suit(tocheck[0])); //should be lowest card in smallest suit
                }

                //[PLAY] empty singleton suit
                if ( num_clubs==1 && this->right->hasClubs && this->left->hasClubs)
                    return this->takecard(card::CLUB);
                if ( num_diamonds==1 && this->right->hasDiamonds && this->left->hasDiamonds)
                    return this->takecard(card::DIAMOND);
                if ( num_hearts==1 && this->right->hasHearts && this->left->hasHearts)
                    return this->takecard(card::HEART);

                //DEFAULT AT END OF FUNCTION
                else { //don't need tricks
                    // play lowest card in suit ->left/->right is not out of
                    tocheck.clear();
                    if (num_clubs>0 && (left->hasClubs || right->hasClubs))
                        tocheck << card::CLUB;
                    if (num_diamonds>0 && (left->hasHearts || right->hasHearts))
                        tocheck << card::DIAMOND;
                    if (num_hearts>0 && (left->hasSpades || right->hasSpades))
                        tocheck << card::HEART;

                    while( tocheck.count() > 1)
                    {
                        if ( cards_in_suit(tocheck[0]) >= cards_in_suit(tocheck[1]) )
                            tocheck.removeAt(0);
                        else tocheck.removeAt(1);
                    }



                }
            }
        }
    } else { //following 
        if (this->isBiddingNil) {
            card::CSUIT lead_suit=trick[0]->suit;
            //play highest card you can play and still be losing
            if (cards_in_suit(lead_suit) > 0) {//followable
                for (int index=0; index < cards_in_suit(lead_suit)-1; ++index) //inherent hand order should make this decreasingly strong
                    if ( current_winning_card(trick,trick_pos) > peekcard(lead_suit,index) )  //



            }
        } else if (this->partner->isBiddingNil) {
            //trick_pos 1: beat if possible, else, lowest card;
            //trick_pos 3: if partner is winning, beat if possible
            //             else, lowest card;
            //trick_pos 2 //shouldn't be here, consider setting partner->isBiddingNil=false when failing
        } else {
            //if (need tricks)
            // win
            //else if loseable
            //  if suit empty and non-spades play largest non-spade
            // else
            //  look backwards from largest to smallest, and play first loser || last card
            //
        }
    }

}


player::player(const QString & n, bool human) : name(n), isHuman(human), score(0)
{   //define bidding type
    this->bid = &player::conservative_bid;
    this->play= &player::conservative_play;
}

void player::setLeft(player * l)
{
    this->left=l;
    l->right=this;
}

void player::setRight(player * r)
{
    this->right=r;
    r->left=this;
}

void player::setPartner(player * p)
{
    this->partner=p;
    p->partner=this;
}

void player::setDealer()
{
    this->isDealer=true;
    this->right->isDealer=false;
    this->left->isDealer=false;
    this->partner->isDealer=false;
}

void player::setScoreboard(QGraphicsSimpleTextItem * b, QGraphicsSimpleTextItem * s)
{
    this->txtBid=b;
    this->partner->txtBid=b;
    this->txtScore=s;
    this->partner->txtScore=s;
}

card::CVALUE player::value_in_suit(const card::CSUIT & suit, int position)
{
    int suit_start_index(-1);
    for(int x(0); x < this->hand.count(); ++x)
        if (hand[x]->suit == suit)
        { suit_start_index=x; break;}
    if ( suit_start_index != -1 && suit_start_index + position < hand.count() )
        return hand[suit_start_index+position]->value;
    return card::NOVALUE;
}
